/*  
 * Actipro Software's WPF DataGrid / Editors Integration Add-on
 * http://actipro.codeplex.com/
 * 
 * Copyright (c) 2009-2015 Actipro Software LLC. 
 *  
 * This source code is subject to the terms of the Microsoft Public License (Ms-PL). 
 *  
 * Redistribution and use in source and binary forms, with or without modification, 
 * is permitted provided that redistributions of the source code retain the above 
 * copyright notices and this file header. 
 *  
 * Additional copyright notices should be appended to the list above. 
 * 
 * For details, see <http://www.opensource.org/licenses/ms-pl.html>. 
 *  
 * All other rights reserved. 
 *
 */  


using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace ActiproSoftware.Windows.Controls.Editors.Interop.DataGrid.Primitives {

	/// <summary>
	/// Represents a base class for data-bound columns for use in a <c>DataGrid</c>.
	/// </summary>
	public abstract class DataGridBoundColumnBase : DataGridBoundColumn {

		#region Dependency Properties

		/// <summary>
		/// Identifies the <see cref="FontFamily"/> dependency property.  This field is read-only.
		/// </summary>
		/// <value>The identifier for the <see cref="FontFamily"/> dependency property.</value>
		public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(DataGridBoundColumnBase),
			new FrameworkPropertyMetadata(SystemFonts.MessageFontFamily, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnNotifyablePropertyValueChanged)));

		/// <summary>
		/// Identifies the <see cref="FontSize"/> dependency property.  This field is read-only.
		/// </summary>
		/// <value>The identifier for the <see cref="FontSize"/> dependency property.</value>
		public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(typeof(DataGridBoundColumnBase),
			new FrameworkPropertyMetadata(SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnNotifyablePropertyValueChanged)));

		/// <summary>
		/// Identifies the <see cref="FontStyle"/> dependency property.  This field is read-only.
		/// </summary>
		/// <value>The identifier for the <see cref="FontStyle"/> dependency property.</value>
		public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(typeof(DataGridBoundColumnBase),
			new FrameworkPropertyMetadata(SystemFonts.MessageFontStyle, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnNotifyablePropertyValueChanged)));

		/// <summary>
		/// Identifies the <see cref="FontWeight"/> dependency property.  This field is read-only.
		/// </summary>
		/// <value>The identifier for the <see cref="FontWeight"/> dependency property.</value>
		public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(typeof(DataGridBoundColumnBase),
			new FrameworkPropertyMetadata(SystemFonts.MessageFontWeight, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnNotifyablePropertyValueChanged)));

		/// <summary>
		/// Identifies the <see cref="Foreground"/> dependency property.  This field is read-only.
		/// </summary>
		/// <value>The identifier for the <see cref="Foreground"/> dependency property.</value>
		public static readonly DependencyProperty ForegroundProperty = TextElement.ForegroundProperty.AddOwner(typeof(DataGridBoundColumnBase),
			new FrameworkPropertyMetadata(SystemColors.ControlTextBrush, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnNotifyablePropertyValueChanged)));

		#endregion // Dependency Properties

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region NON-PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Compares the default values of the specified metadata.
		/// </summary>
		/// <param name="metadata1">The first metadata.</param>
		/// <param name="metadata2">The second metadata.</param>
		/// <returns><c>true</c> if the the default values of the specified metadata are equal; otherwise, <c>false</c>.</returns>
		private static bool DefaultValuesEqual(PropertyMetadata metadata1, PropertyMetadata metadata2) {
			if (metadata1 == null || metadata2 == null)
				return true;
			return object.Equals(metadata1.DefaultValue, metadata2.DefaultValue);
		}

		#endregion // NON-PUBLIC PROCEDURES

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		#region PUBLIC PROCEDURES
		/////////////////////////////////////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Associates the binding expression, defined by the <c>Binding</c> property, with the specified target property.
		/// </summary>
		/// <param name="targetElement">The target element.</param>
		/// <param name="targetProperty">The target property.</param>
		[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
		protected void ApplyBinding(FrameworkElement targetElement, DependencyProperty targetProperty) {
			BindingBase binding = this.Binding;
			if (null != binding)
				BindingOperations.SetBinding(targetElement, targetProperty, binding);
			else
				BindingOperations.ClearBinding(targetElement, targetProperty);
		}

		/// <summary>
		/// Applies standard values to the specified target element.
		/// </summary>
		/// <param name="targetElement">The target element.</param>
		protected virtual void ApplyStandardValues(FrameworkElement targetElement) {
			this.ApplyValue(FontFamilyProperty, targetElement, TextElement.FontFamilyProperty);
			this.ApplyValue(FontSizeProperty, targetElement, TextElement.FontSizeProperty);
			this.ApplyValue(FontStyleProperty, targetElement, TextElement.FontStyleProperty);
			this.ApplyValue(FontWeightProperty, targetElement, TextElement.FontWeightProperty);
			this.ApplyValue(ForegroundProperty, targetElement, TextElement.ForegroundProperty);
		}

		/// <summary>
		/// Sets the value of the specified target object and property, based on the specified source property.
		/// </summary>
		/// <param name="sourceProperty">The source property.</param>
		/// <param name="targetElement">The target element.</param>
		/// <param name="targetProperty">The target property.</param>
		protected virtual void ApplyValue(DependencyProperty sourceProperty, FrameworkElement targetElement, DependencyProperty targetProperty) {
			if (sourceProperty == null)
				throw new ArgumentNullException("sourceProperty");
			else if (targetElement == null)
				throw new ArgumentNullException("targetElement");
			else if (targetProperty == null)
				throw new ArgumentNullException("targetProperty");

			// 8/22/2011 - If the property is not set on the column, then do not pass down to the element (10F-15E35C2E-480E)
			// 1/27/2012 - Fixed issue with column default that differ from the column not getting passed down properly (197-16B573BD-0301)
			if (DependencyPropertyHelper.GetValueSource(this, sourceProperty).BaseValueSource == BaseValueSource.Default &&
				DefaultValuesEqual(sourceProperty.GetMetadata(this), targetProperty.GetMetadata(targetElement)))
				targetElement.ClearValue(targetProperty);
			else
				targetElement.SetValue(targetProperty, this.GetValue(sourceProperty));
		}

		/// <summary>
		/// Gets or sets the font family.
		/// This is a dependency property.
		/// </summary>
		/// <value>
		/// The font family.
		/// The default value is <c>SystemFonts.MessageFontFamily</c>.
		/// </value>
		public FontFamily FontFamily {
			get { return (FontFamily)this.GetValue(DataGridBoundColumnBase.FontFamilyProperty); }
			set { this.SetValue(DataGridBoundColumnBase.FontFamilyProperty, value); }
		}

		/// <summary>
		/// Gets or sets the font size.
		/// This is a dependency property.
		/// </summary>
		/// <value>
		/// The font size.
		/// The default value is <c>SystemFonts.MessageFontSize</c>.
		/// </value>
		[Localizability(LocalizationCategory.None), TypeConverter(typeof(FontSizeConverter))]
		public double FontSize {
			get { return (double)this.GetValue(DataGridBoundColumnBase.FontSizeProperty); }
			set { this.SetValue(DataGridBoundColumnBase.FontSizeProperty, value); }
		}

		/// <summary>
		/// Gets or sets the font style.
		/// This is a dependency property.
		/// </summary>
		/// <value>
		/// The font style.
		/// The default value is <c>SystemFonts.MessageFontStyle</c>.
		/// </value>
		public FontStyle FontStyle {
			get { return (FontStyle)this.GetValue(DataGridBoundColumnBase.FontStyleProperty); }
			set { this.SetValue(DataGridBoundColumnBase.FontStyleProperty, value); }
		}

		/// <summary>
		/// Gets or sets the font weight.
		/// This is a dependency property.
		/// </summary>
		/// <value>
		/// The font weight.
		/// The default value is <c>SystemFonts.MessageFontWeight</c>.
		/// </value>
		public FontWeight FontWeight {
			get { return (FontWeight)this.GetValue(DataGridBoundColumnBase.FontWeightProperty); }
			set { this.SetValue(DataGridBoundColumnBase.FontWeightProperty, value); }
		}

		/// <summary>
		/// Gets or sets the foreground brush.
		/// This is a dependency property.
		/// </summary>
		/// <value>
		/// The font foreground brush.
		/// The default value is <c>SystemColors.ControlTextBrush</c>.
		/// </value>
		public Brush Foreground {
			get { return (Brush)this.GetValue(DataGridBoundColumnBase.ForegroundProperty); }
			set { this.SetValue(DataGridBoundColumnBase.ForegroundProperty, value); }
		}

		/// <summary>
		/// Gets the <c>DependencyProperty</c> associated with the specified name.
		/// </summary>
		/// <param name="propertyName">Name of the property.</param>
		/// <returns>The <c>DependencyProperty</c> associated with the specified name.</returns>
		protected virtual DependencyProperty GetProperty(string propertyName) {
			DependencyPropertyDescriptor propertyDescriptor = DependencyPropertyDescriptor.FromName(propertyName, this.GetType(), this.GetType());
			if (null != propertyDescriptor)
				return propertyDescriptor.DependencyProperty;
			return null;
		}

		/// <summary>
		/// Determines whether the left mouse button is down based on the specified <see cref="MouseButtonEventArgs"/>.
		/// </summary>
		/// <param name="args">The <see cref="MouseButtonEventArgs"/> instance containing the event data.</param>
		/// <returns>
		/// <c>true</c> if the left mouse button is down based on the specified <see cref="MouseButtonEventArgs"/>; otherwise, <c>false</c>.
		/// </returns>
		protected static bool IsMouseLeftButtonDown(MouseButtonEventArgs args) {
			return (null != args && MouseButton.Left == args.ChangedButton && MouseButtonState.Pressed == args.ButtonState);
		}

		/// <summary>
		/// Occurs when a property value changes that requires the column to be notified.
		/// </summary>
		/// <param name="d">The <see cref="DependencyObject"/> whose property is changed.</param>
		/// <param name="e">A <see cref="DependencyPropertyChangedEventArgs"/> that contains the event data.</param>
		[SuppressMessage ("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="d")]
		[SuppressMessage ("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="e")]
		protected static void OnNotifyablePropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			DataGridBoundColumnBase control = d as DataGridBoundColumnBase;
			if (null != control)
				control.NotifyPropertyChanged(e.Property.Name);
		}

		/// <summary>
		/// Refreshes the contents of a cell in the column in response to a column property value change.
		/// </summary>
		/// <param name="element">The element returned by the <c>Content</c> property of the cell to refresh.</param>
		/// <param name="propertyName">The name of the column property that has changed.</param>
		// 10/26/2011 - Ensure that changes to the columns are passed down to the underlying editors (http://www.actiprosoftware.com/support/forums/viewforumtopic.aspx?ForumTopicID=6087)
		protected override void RefreshCellContent(FrameworkElement element, string propertyName) {
			DataGridCell cell = element as DataGridCell;
			if (cell != null) {
				FrameworkElement targetElement = cell.Content as FrameworkElement;
				if (null != targetElement) {
					DependencyProperty property = this.GetProperty(propertyName);
					if (null != property)
						this.ApplyValue(property, targetElement, property);
				}
			}
			base.RefreshCellContent(element, propertyName);
		}

		/// <summary>
		/// Updates the source of the <c>BindingExpression</c> associated with the specified element/property.
		/// </summary>
		/// <param name="element">The element.</param>
		/// <param name="property">The property.</param>
		protected static void UpdateBindingSource(FrameworkElement element, DependencyProperty property) {
			if (null != element) {
				BindingExpression bindingExpression = element.GetBindingExpression(property);
				if (null != bindingExpression)
					bindingExpression.UpdateSource();
			}
		}

		/// <summary>
		/// Updates the target of the <c>BindingExpression</c> associated with the specified element/property.
		/// </summary>
		/// <param name="element">The element.</param>
		/// <param name="property">The property.</param>
		protected static void UpdateBindingTarget(FrameworkElement element, DependencyProperty property) {
			if (null != element) {
				BindingExpression bindingExpression = element.GetBindingExpression(property);
				if (null != bindingExpression)
					bindingExpression.UpdateTarget();
			}
		}

		#endregion // PUBLIC PROCEDURES
	}
}
